#ifndef RPC_MPRPC_CHANNEL_H
#define RPC_MPRPC_CHANNEL_H

#include <google/protobuf/descriptor.h>
#include <google/protobuf/message.h>
#include <google/protobuf/service.h>
#include <algorithm>
#include <functional>
#include <iostream>
#include <map>
#include <random>
#include <string>
#include <unordered_map>
#include <vector>

namespace rpc {
/**
 * @brief RPC客户端通信通道类，继承自Protobuf的RpcChannel
 * 
 * 该类是Protobuf RPC框架客户端的核心通信组件，负责实现RPC调用的底层网络细节，
 * 是客户端 stub 代理对象与服务端之间的"通信管道"。主要功能包括：
 * 1. 接收 stub 发起的RPC调用请求（通过CallMethod方法）
 * 2. 对请求消息进行序列化（按自定义协议组织数据）
 * 3. 建立并维护与服务端的网络连接（通过文件描述符管理）
 * 4. 发送序列化后的请求数据到服务端
 * 5. 接收服务端的响应数据并反序列化到响应消息中
 * 6. 处理连接异常、重连等网络层问题
 * 
 * 继承自google::protobuf::RpcChannel的设计，使其能无缝集成到Protobuf的RPC框架中，
 * 符合Protobuf对RPC通道的接口规范。
 */
class MprpcChannel : public google::protobuf::RpcChannel {
public:
    /**
     * @brief 重写Protobuf的RpcChannel接口，处理所有RPC调用的核心方法
     * 
     * 当客户端通过stub代理对象调用远程方法时，最终会触发该方法。
     * 其核心职责是将RPC调用转换为网络请求，并处理响应。
     * 
     * @param method 方法描述符，包含远程方法的元信息（如服务名、方法名、参数类型等）
     * @param controller RPC控制器，用于控制RPC调用过程（如设置超时、错误信息传递）
     * @param request 请求消息对象，包含客户端传入的调用参数（已填充具体数据）
     * @param response 响应消息对象，用于存储服务端返回的结果（由该方法填充）
     * @param done 回调函数，当RPC调用完成（无论成功或失败）后触发（通常用于异步调用）
     * 
     * 方法流程大致为：
     * 1. 根据method获取服务名和方法名，确定目标服务端
     * 2. 序列化request对象为字节流
     * 3. 按自定义RPC协议组织数据（如添加消息头、长度等）
     * 4. 通过m_clientFd发送数据到服务端
     * 5. 等待并接收服务端响应
     * 6. 反序列化响应数据到response对象
     * 7. 若有done回调，触发回调函数通知调用完成
     */
    void CallMethod(const google::protobuf::MethodDescriptor *method, 
                    google::protobuf::RpcController *controller,
                    const google::protobuf::Message *request, 
                    google::protobuf::Message *response,
                    google::protobuf::Closure *done) override;
    
    /**
     * @brief 构造函数，初始化通道并指定目标服务端地址
     * 
     * @param ip 目标服务端的IP地址（字符串形式，如"127.0.0.1"）
     * @param port 目标服务端的端口号（本机字节序）
     * @param connectNow 是否立即与服务端建立连接（true：构造时连接；false：延迟到第一次调用时连接）
     * 
     * 构造函数会保存目标服务端的IP和端口（m_ip、m_port），并根据connectNow决定是否立即调用newConnect建立连接。
     */
    MprpcChannel(std::string ip, short port, bool connectNow);

private:
    /**
     * @brief 与目标服务端建立TCP连接，并初始化m_clientFd
     * 
     * 该方法封装了TCP连接的底层细节（如socket创建、connect调用等），是通道建立网络连接的核心工具。
     * 
     * @param ip 目标服务端的IP地址（字符串形式，本机字节序）
     * @param port 目标服务端的端口号（本机字节序）
     * @param errMsg 输出参数，若连接失败则存储失败原因（如"连接超时"、"拒绝连接"等）
     * @return 连接成功返回true，失败返回false（此时errMsg有效）
     * 
     * 流程说明：
     * 1. 创建TCP套接字（socket()）
     * 2. 填充服务端地址结构体（sockaddr_in）
     * 3. 调用connect()与服务端建立连接
     * 4. 若成功，将套接字句柄赋值给m_clientFd；若失败，记录错误信息到errMsg
     */
    bool newConnect(const char *ip, uint16_t port, std::string *errMsg);

private:
    int m_clientFd;             ///< 客户端网络连接的文件描述符，用于与服务端通信（-1表示未连接）
    const std::string m_ip;     ///< 目标服务端的IP地址（常量，一旦初始化不可修改），用于连接和重连
    const uint16_t m_port;      ///< 目标服务端的端口号（常量，一旦初始化不可修改），用于连接和重连
};

}   // namespace rpc

#endif  // RPC_MPRPC_CHANNEL_H